/**
 * @Author       : Hejh(3010733382@qq.com)
 * @Version      : V1.0
 * @Date         : 2024-02-29 19:20:40
 * @Description  : 角色的文档角色权限验证
 */

const { ERROR_SYSTEM_CODE } = require('@/constants/errorCode')
const { SEPARATE_CHAR } = require('@/constants/index')
const { getUserPermissionsById } = require('@/controller/userController')
const R = require('@/models/Response')
const {
  Source,
  SourceAuth,
  Role,
  Permission,
  User,
  Department,
  RolePermission,
  UserDepartment
} = require('@/models/model')
const { Op } = require('sequelize')

SourceAuth.belongsTo(Role, { foreignKey: 'role_id' })

/**
 * 检查创建、移动、复制时用户是否拥有对parentId对应文件夹的操作权限
 */
module.exports.checkActionParentId = async (parentId, userId) => {
  const source = await Source.findByPk(BigInt(parentId))

  // 检查source-auth中是否存在
  const sa1 = await SourceAuth.findOne({
    where: {
      sourceId: BigInt(source.id),
      targetType: 1,
      targetId: BigInt(userId)
    },
    include: [
      {
        model: Role,
        include: [
          {
            model: Permission,
            through: {
              attributes: []
            }
          }
        ]
      }
    ]
  })

  if (sa1) {
    return
  }

  // 检查source-auth了parent的父代没
  const fatherIds = source.parentLevel.split(',').map((item) => BigInt(item))
  const sa2 = await SourceAuth.findAll({
    where: {
      sourceId: {
        [Op.in]: fatherIds
      },
      targetType: 1,
      targetId: BigInt(userId)
    },
    include: [
      {
        model: Role,
        include: [
          {
            model: Permission,
            through: {
              attributes: []
            }
          }
        ]
      }
    ]
  })

  // 看记录中的权限是否有
  if (sa2.length) {
    return
  }

  // 看授权给了用户的部门没
  // 查询用户所在部门，看部门是否被授权
  await User.findByPk(BigInt(userId), {
    include: [
      {
        model: Department,
        through: {
          attributes: []
        }
      }
    ]
  })

  // 看用户是否属于该部门，继承在该部门的默认权限
}

/**
 * 验证用户对parentId下文件的list权限
 * 1. parentId 个人文件---->需要自己的
 * 2. parentId 部门文件-----> 需要source-auth对应的权限（可以父级继承）（可以子孙级存在）(就近原则),
 * 或者授权给部门的权限        或者用户处于该部门的权限
 *
 * @param {*}
 * @returns
 */
module.exports.verifyListAuth = async (ctx, next) => {
  // 获取用户的权限列表，判断是否拥有该权限
  const user = await getUserPermissionsById(ctx.user.id)
  const isPass = user.role.Permissions.some((item) => {
    return auth.includes(item.ename)
  })

  if (!isPass) {
    ctx.body = R.error(ERROR_SYSTEM_CODE.PERMISSION_ERROR)
    return
  }

  await next()
}

/**
 * 根据roleId获取权限列表
 * @param {*} roleId
 */
module.exports.getPermissionsByRoleId = async (roleId) => {
  const res = await RolePermission.findAll({
    where: {
      roleId: BigInt(roleId)
    },
    include: [
      {
        model: Permission,
        attributes: ['id', 'ename', 'name']
      }
    ]
  })

  const permissions = res.map((item) => {
    return item.Permission
  })

  return permissions
}

/**
 * 获取用户所在的所有部门，包括父级部门
 */
module.exports.getUserAllDeps = async (userId) => {
  let totalDep = []
  const userDeps = await UserDepartment.findAll({
    where: {
      userId: BigInt(userId)
    },
    include: [
      {
        model: Department
      }
    ]
  })

  totalDep = totalDep.concat(userDeps.map((item) => item.Department.id))

  // 及其父级部门
  const fDeps1 = userDeps.map((item) => item.Department.parentLevel)
  fDeps1.forEach((item) => {
    totalDep = totalDep.concat(item.split(SEPARATE_CHAR))
  })

  totalDep = [...new Set(totalDep.filter((item) => item !== '-1'))]

  return totalDep
}

/**
 * 验证用户对某个资源是否拥有某种权限，针对于部门文件
 * @param {*} userId 用户ID
 * @param {*} sourceId 资源ID,
 * @param {*} auth 权限列表
 * @returns {boolean}
 */
module.exports.commonVerifySourceAuth = async (userId, sourceId, auth) => {
  // 获取用户的角色ID，如果为系统管理员，不受限制
  const user = await User.findByPk(BigInt(userId))
  if (+user.roleId === 1) {
    return true
  }

  let newSourceId = sourceId
  while (newSourceId !== '-1') {
    // 1. 查询source-auth表
    const sourceUser = await SourceAuth.findOne({
      where: {
        sourceId: BigInt(newSourceId),
        targetId: BigInt(userId),
        targetType: 1
      }
    })

    if (sourceUser) {
      //根据roleId获取权限列表，看auth是否include其中一项
      const permissions = await this.getPermissionsByRoleId(sourceUser.roleId)

      const isPass = permissions.some((item) => {
        return auth.includes(item.ename)
      })

      if (isPass) {
        return true
      } else {
        return false
      }
    }

    // 说明当前没有，获取当前source的父source
    const curSource = await Source.findByPk(BigInt(newSourceId))
    newSourceId = curSource.parentId
  }

  // TODO：根据情况的不同，可能需要打通下方

  // 说明对文件夹及其父文件夹都没有设置权限
  // 2. 文件夹可能会对部门进行授权，这个部门不一定是当前资源所在部门，可以是其他部门
  let newSourceId2 = sourceId
  // 获取用户所在部门及父级部门
  const userAllDeps = await this.getUserAllDeps(userId)
  while (newSourceId2 !== '-1') {
    const sourceDeps = await SourceAuth.findAll({
      where: {
        sourceId: BigInt(newSourceId),
        targetType: 2
      }
    })

    // 筛选掉不在用户所在部门的
    const finalDeps = sourceDeps.filter((item) => {
      return userAllDeps.includes(item.targetId)
    })

    // 看授权的角色ID是否包含它
    if (finalDeps.length) {
      // 用户属于a,b部门，b的父亲：c
      // 至少授权了一个部门，a,c
      //
      // 对于用户所在的所有部门，如果所有的授权都不包含，
      let isAllPass = false
      for (let i = 0; i < finalDeps.length; i++) {
        const permissions = await this.getPermissionsByRoleId(
          finalDeps[i].roleId
        )

        const isPass = permissions.some((item) => {
          return auth.includes(item.ename)
        })

        if (isPass) {
          isAllPass = true
          break
        }
      }
      if (isAllPass) {
        return true
      }
    }

    // 说明当前没有，获取当前source的父source
    const curSource = await Source.findByPk(BigInt(newSourceId2))
    newSourceId2 = curSource.parentId
  }

  // 到这里，说明source-auth没有任何记录，这个时候就看用户在不在当前source所在部门及其父部门了
  const targetSource = await Source.findByPk(BigInt(sourceId))
  const targetDep = await Department.findByPk(BigInt(targetSource.targetId))

  const targetDeps = [targetDep.id]
    .concat(targetDep.parentLevel.split(SEPARATE_CHAR))
    .filter((item) => item !== '-1')
    .map((v) => BigInt(v))

  let res1 = await UserDepartment.findOne({
    where: {
      userId: BigInt(userId),
      departmentId: {
        [Op.in]: targetDeps
      }
    }
  })

  res1 = [res1]

  const finaRoleIds = [...new Set(res1.map((item) => item.roleId))]

  for (let i = 0; i < finaRoleIds.length; i++) {
    const permissions = await this.getPermissionsByRoleId(finaRoleIds[i])

    const isPass = permissions.some((item) => {
      return auth.includes(item.ename)
    })

    if (isPass) {
      return true
    }
  }

  // 1. 验证对用户授权没
  // 2. 验证对用户所在部门授权没
  // 3. 用户在文件所在部门没

  // 到这里，如果存在权限（true），还需要处理，例如文件放入回收站，可能对一sourceId拥有bin权限，
  // 但如果存在sourceId的子文件不允许用户回收，又该如何处理？

  return false
}

module.exports.commonVerifySourcesAuth = async (userId, sourceIds, auth) => {
  for (let i = 0; i < sourceIds.length; i++) {
    const res = await this.commonVerifySourceAuth(userId, sourceIds[i], auth)
    if (!res) {
      return false
    }
  }
  return true
}

/**
 * 需要开发一个接口，传入用户ID，资源ID数组，返回用户对这个资源都拥有的权限数组
 */

module.exports.getUserSourcesAuths = async (userId, sourceIds) => {
  // 获取全部的permissions
  let finaPermissions = await Permission.findAll({
    where: {
      ename: {
        [Op.like]: 'doc.auth.' + '%'
      }
    }
  })

  // 获取用户的角色ID，如果为系统管理员，不受限制
  const user = await User.findByPk(BigInt(userId))
  if (+user.roleId === 1) {
    return finaPermissions
  }

  for (let i = 0; i < sourceIds.length; i++) {
    const mySource = await Source.findByPk(BigInt(sourceIds[i]))
    if (+mySource.targetType === 1) {
      continue
    }

    if (!finaPermissions.length) {
      break
    }

    let newSourceId = sourceIds[i]
    while (newSourceId !== '-1') {
      // 1. 查询source-auth表
      const sourceUser = await SourceAuth.findOne({
        where: {
          sourceId: BigInt(newSourceId),
          targetId: BigInt(userId),
          targetType: 1
        }
      })

      if (sourceUser) {
        //根据roleId获取权限列表，看auth是否include其中一项
        const permissions = await this.getPermissionsByRoleId(sourceUser.roleId)
        // 保留当前资源的权限数组
        finaPermissions = finaPermissions.filter((item) => {
          return permissions.some((v) => v.id === item.id)
        })

        // continue
        break
      }

      // 说明当前没有，获取当前source的父source
      const curSource = await Source.findByPk(BigInt(newSourceId))
      if (!curSource) {
        newSourceId = '-1'
      } else {
        newSourceId = curSource.parentId
      }
    }

    // TODO：根据情况的不同，可能需要打通下方

    // 说明对文件夹及其父文件夹都没有设置权限
    // 2. 文件夹可能会对部门进行授权，这个部门不一定是当前资源所在部门，可以是其他部门
    let newSourceId2 = sourceIds[i]
    // 获取用户所在部门及父级部门
    const userAllDeps = await this.getUserAllDeps(userId)
    while (newSourceId2 !== '-1') {
      const sourceDeps = await SourceAuth.findAll({
        where: {
          sourceId: BigInt(newSourceId),
          targetType: 2
        }
      })

      // 筛选掉不在用户所在部门的
      const finalDeps = sourceDeps.filter((item) => {
        return userAllDeps.includes(item.targetId)
      })

      // 看授权的角色ID是否包含它
      if (finalDeps.length) {
        // 用户属于a,b部门，b的父亲：c
        // 至少授权了一个部门，a,c
        //
        // 对于用户所在的所有部门，如果所有的授权都不包含，
        // 这里应该取交集
        const authSet = new Set()
        for (let i = 0; i < finalDeps.length; i++) {
          const permissions = await this.getPermissionsByRoleId(
            finalDeps[i].roleId
          )
          permissions.forEach((item) => {
            authSet.add(item.id)
          })
        }

        const newPermission = [...authSet]

        // 继续去除
        finaPermissions = finaPermissions.filter((item) => {
          return newPermission.some((v) => v === item.id)
        })

        // continue
        break
      }

      // 说明当前没有，获取当前source的父source
      const curSource = await Source.findByPk(BigInt(newSourceId2))
      if (!curSource) {
        newSourceId2 = '-1'
      } else {
        newSourceId2 = curSource.parentId
      }
    }

    // 到这里，说明source-auth没有任何记录，这个时候就看用户在不在当前source所在部门及其父部门了
    const targetSource = await Source.findByPk(BigInt(sourceIds[i]))
    const targetDep = await Department.findByPk(BigInt(targetSource.targetId))

    const targetDeps = [targetDep.id]
      .concat(targetDep.parentLevel.split(SEPARATE_CHAR))
      .filter((item) => item !== '-1')
      .map((v) => BigInt(v))

    let res1 = await UserDepartment.findOne({
      where: {
        userId: BigInt(userId),
        departmentId: {
          [Op.in]: targetDeps
        }
      }
    })

    res1 = [res1]

    const finaRoleIds = [...new Set(res1.map((item) => item.roleId))]

    // 用户可在当前文件所在部门，或者其父部门，也是取交集
    const authSet = new Set()
    for (let i = 0; i < finaRoleIds.length; i++) {
      const permissions = await this.getPermissionsByRoleId(finaRoleIds[i])
      permissions.forEach((item) => {
        authSet.add(item.id)
      })
    }

    const newPermission = [...authSet]

    // 继续去除
    finaPermissions = finaPermissions.filter((item) => {
      return newPermission.some((v) => v === item.id)
    })
  }

  return finaPermissions
}

/**
 * 开发一个接口，传入用户ID，资源ID，返回用户对这个资源的文档角色
 * @param {} userId
 * @param {*} sourceIds
 * @returns
 */
module.exports.getUserSourceRole = async (userId, sourceId) => {
  // 获取用户的角色ID，如果为系统管理员，不受限制

  const commonIncludes = {
    include: [
      {
        model: Permission,
        attributes: ['id', 'ename', 'name']
      }
    ]
  }
  const user = await User.findByPk(BigInt(userId))
  if (+user.roleId === 1) {
    const role = await Role.findByPk(BigInt(9), commonIncludes)
    return role
  }

  const mySource = await Source.findByPk(BigInt(sourceId))
  if (+mySource.targetType === 1) {
    const role = await Role.findByPk(BigInt(9), commonIncludes)
    return role
  }

  let newSourceId = sourceId
  while (newSourceId !== '-1') {
    // 1. 查询source-auth表
    const sourceUser = await SourceAuth.findOne({
      where: {
        sourceId: BigInt(newSourceId),
        targetId: BigInt(userId),
        targetType: 1
      }
    })

    if (sourceUser) {
      //根据roleId获取权限列表，看auth是否include其中一项
      const role = await Role.findByPk(
        BigInt(sourceUser.roleId),
        commonIncludes
      )
      // 保留当前资源的权限数组
      return role
    }

    // 说明当前没有，获取当前source的父source
    const curSource = await Source.findByPk(BigInt(newSourceId))
    newSourceId = curSource.parentId
  }

  // TODO：根据情况的不同，可能需要打通下方

  // 说明对文件夹及其父文件夹都没有设置权限
  // 2. 文件夹可能会对部门进行授权，这个部门不一定是当前资源所在部门，可以是其他部门
  let newSourceId2 = sourceId
  // 获取用户所在部门及父级部门
  const userAllDeps = await this.getUserAllDeps(userId)
  while (newSourceId2 !== '-1') {
    const sourceDeps = await SourceAuth.findAll({
      where: {
        sourceId: BigInt(newSourceId),
        targetType: 2
      }
    })

    // 筛选掉不在用户所在部门的
    const finalDeps = sourceDeps.filter((item) => {
      return userAllDeps.includes(item.targetId)
    })

    // 可以进行父子依赖分析

    // 看授权的角色ID是否包含它
    if (finalDeps.length) {
      for (let i = 0; i < finalDeps.length; i++) {
        const role = await Role.findByPk(
          BigInt(finalDeps[i].roleId),
          commonIncludes
        )
        return role
      }
    }

    // 说明当前没有，获取当前source的父source
    const curSource = await Source.findByPk(BigInt(newSourceId2))
    newSourceId2 = curSource.parentId
  }

  // 到这里，说明source-auth没有任何记录，这个时候就看用户在不在当前source所在部门及其父部门了
  const targetSource = await Source.findByPk(BigInt(sourceId))
  const targetDep = await Department.findByPk(BigInt(targetSource.targetId))

  const targetDeps = [targetDep.id]
    .concat(targetDep.parentLevel.split(SEPARATE_CHAR))
    .filter((item) => item !== '-1')
    .map((v) => BigInt(v))

  for (let i = 0; i < targetDeps.length; i++) {
    const res1 = await UserDepartment.findOne({
      where: {
        userId: BigInt(userId),
        departmentId: targetDeps[i]
      }
    })

    if (res1) {
      const role = await Role.findByPk(BigInt(res1.roleId), commonIncludes)
      return role
    }
  }

  // 到这里，说明权限为无权限
  return {
    id: '-1',
    ename: 'noAuth',
    name: '无权限',
    label: '#f7f7f7',
    Permissions: []
  }
}
